﻿using ISysServiceProvider = System.IServiceProvider;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

namespace Company.PackageExtjs
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Drawing;
    using System.Drawing.Text;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Security.Permissions;
    using System.Windows.Forms;

    using EnvDTE;

    using FastColoredTextBoxNS;

    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.OLE.Interop;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.TextManager.Interop;

    using tom;

    using IDataObject = Microsoft.VisualStudio.OLE.Interop.IDataObject;
    using SelectionContainer = Microsoft.VisualStudio.Shell.SelectionContainer;

    /// <summary>
    /// This control host the editor (an extended RichTextBox) and is responsible for
    /// handling the commands targeted to the editor as well as saving and loading
    /// the document. This control also implement the search and replace functionalities.
    /// </summary>
    ///////////////////////////////////////////////////////////////////////////////
    // Having an entry in the new file dialog.
    //
    // For our file type should appear under "General" in the new files dialog, we need the following:-
    //     - A .vsdir file in the same directory as NewFileItems.vsdir (generally under Common7\IDE\NewFileItems).
    //       In our case the file name is Editor.vsdir but we only require a file with .vsdir extension.
    //     - An empty myc file in the same directory as NewFileItems.vsdir. In
    //       our case we chose MyExtFile.myc. Note this file name appears in Editor.vsdir
    //       (see vsdir file format below)
    //     - Three text strings in our language specific resource. File Resources.resx :-
    //          - "Rich Text file" - this is shown next to our icon.
    //          - "A blank rich text file" - shown in the description window
    //             in the new file dialog.
    //          - "MyExtFile" - This is the base file name. New files will initially
    //             be named as MyExtFile1.myc, MyExtFile2.myc... etc.
    ///////////////////////////////////////////////////////////////////////////////
    // Editor.vsdir contents:-
    //    MyExtFile.myc|{3085E1D6-A938-478e-BE49-3546C09A1AB1}|#106|80|#109|0|401|0|#107
    //
    // The fields in order are as follows:-
    //    - MyExtFile.myc - our empty myc file
    //    - {db16ff5e-400a-4cb7-9fde-cb3eab9d22d2} - our Editor package guid
    //    - #106 - the ID of "Rich Text file" in the resource
    //    - 80 - the display ordering priority
    //    - #109 - the ID of "A blank rich text file" in the resource
    //    - 0 - resource dll string (we don't use this)
    //    - 401 - the ID of our icon
    //    - 0 - various flags (we don't use this - se vsshell.idl)
    //    - #107 - the ID of "myc"
    ///////////////////////////////////////////////////////////////////////////////
    //This is required for Find In files scenario to work properly. This provides a connection point 
    //to the event interface
    [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
    [ComSourceInterfaces(typeof(IVsTextViewEvents))]
    [ComVisible(true)]
    public sealed class EditorPane : WindowPane,
                                     IVsPersistDocData,
        //IVsTextViewEvents,
                                     //to Enable persistence functionality for document data
                                     IPersistFileFormat,
                                     //to enable the programmatic loading or saving of an object 
                                     //in a format specified by the user.
                                         //                               IVsFileChangeEvents,
                                     //to notify the client when file changes on disk
                                        //                                 IVsDocDataFileChangeControl,
                                     //to Determine whether changes to files made outside 
                                     //of the editor should be ignored
                                         //                              IVsFileBackup,
                                     //to support backup of files. Visual Studio File Recovery 
                                     //backs up all objects in the Running Document Table that 
                                     //support IVsFileBackup and have unsaved changes.
                                          //                                 IVsStatusbarUser,
                                     //support updating the status bar
                                           //                             IVsFindTarget,
                                     //to implement find and replace capabilities within the editor
                                          //                                IVsTextImage,
                                     //to support find and replace in a text image
                                           //                               IVsTextSpanSet,
                                     //to support find and replace in a text image
                                             //                             IVsTextBuffer,
                                     //needed for Find and Replace to work appropriately
                                           //                               IVsTextView,
                                     //needed for Find and Replace to work appropriately
                                           //                               IVsCodeWindow,
                                     //needed for Find and Replace to work appropriately
                                          //                                IVsTextLines,
                                     //needed for Find and Replace to work appropriately
                                           //                               IExtensibleObject,
                                     //so we can get the automation object
                                     IEditor //the automation interface for Editor
                                         //  IVsToolboxUser //Sends notification about Toolbox items to the owner of these items
    {
        #region Constants and Fields

        private const string MyExtension = ".js";

        private const uint MyFormat = 0;

        private static readonly string[] fontSizeArray =
            {
                "8", "9", "10", "11", "12", "14", "16", "18", "20", "22",
                "24", "26", "28", "36", "48", "72"
            };

        private readonly ArrayList textSpanArray = new ArrayList();

        private Timer FNFStatusbarTrigger = new Timer();

        private Timer FileChangeTrigger = new Timer();

        private bool backupObsolete = true;

        private EditorControl editorControl;

        private IExtensibleObjectSite extensibleObjectSite;

        private bool fileChangedTimerSet;

        private string fileName = string.Empty;

        private object findState;

        private string[] fontListArray;

        private bool gettingCheckoutStatus;

        private int ignoreFileChangeLevel;

        private bool isDirty;

        // Flag true when we are loading the file. It is used to avoid to change the isDirty flag
        // when the changes are related to the load operation.
        private bool loading;

        private bool lockImage;

        private PackageExtjsPackage myPackage;

        // This flag is true when we are asking the QueryEditQuerySave service if we can edit the
        // file. It is used to avoid to have more than one request queued.

        private SelectionContainer selContainer;

        private IVsTextImage spTextImage;

        private ITrackSelection trackSel;

        private uint vsFileChangeCookie;

        private IVsFileChangeEx vsFileChangeEx;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Constructor that calls the Microsoft.VisualStudio.Shell.WindowPane constructor then
        /// our initialization functions.
        /// </summary>
        /// <param name="package">Our Package instance.</param>
        public EditorPane(PackageExtjsPackage package)
            : base(null)
        {
            this.PrivateInit(package);
        }

        public override IWin32Window Window
        {
            get
            {
                return this.editorControl;
            }
        }

        private void PrivateInit(PackageExtjsPackage package)
        {
            this.myPackage = package;
            this.loading = false;
            this.gettingCheckoutStatus = false;
            this.trackSel = null;

           

            Control.CheckForIllegalCrossThreadCalls = false;
            // Create an ArrayList to store the objects that can be selected
         


            // Create and initialize the editor

            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EditorPane));
            this.editorControl = new EditorControl();

            resources.ApplyResources(this.editorControl, "editorControl", CultureInfo.CurrentUICulture);

        }

        #endregion

        public int GetGuidEditorType(out Guid pClassID)
        {
            return ((IPersistFileFormat)this).GetClassID(out pClassID);
        }

        public int IsDocDataDirty(out int pfDirty)
        {
            return ((IPersistFileFormat)this).IsDirty(out pfDirty);
        }

        public int SetUntitledDocPath(string pszDocDataPath)
        {
            return ((IPersistFileFormat)this).InitNew(MyFormat);
        }

        public int LoadDocData(string pszMkDocument)
        {
            return ((IPersistFileFormat)this).Load(pszMkDocument, 0, 0);
        }

        public int SaveDocData(VSSAVEFLAGS dwSave, out string pbstrMkDocumentNew, out int pfSaveCanceled)
        {
            pbstrMkDocumentNew = null;
            pfSaveCanceled = 0;
            int hr = VSConstants.S_OK;

            switch (dwSave)
            {
                case VSSAVEFLAGS.VSSAVE_Save:
                case VSSAVEFLAGS.VSSAVE_SilentSave:
                    {
                        IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)GetService(typeof(SVsQueryEditQuerySave));

                        // Call QueryEditQuerySave
                        uint result = 0;
                        hr = queryEditQuerySave.QuerySaveFile(
                                fileName,        // filename
                                0,    // flags
                                null,            // file attributes
                                out result);    // result
                        if (ErrorHandler.Failed(hr))
                            return hr;

                        // Process according to result from QuerySave
                        switch ((tagVSQuerySaveResult)result)
                        {
                            case tagVSQuerySaveResult.QSR_NoSave_Cancel:
                                // Note that this is also case tagVSQuerySaveResult.QSR_NoSave_UserCanceled because these
                                // two tags have the same value.
                                pfSaveCanceled = ~0;
                                break;

                            case tagVSQuerySaveResult.QSR_SaveOK:
                                {
                                    // Call the shell to do the save for us
                                    IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
                                    hr = uiShell.SaveDocDataToFile(dwSave, (IPersistFileFormat)this, fileName, out pbstrMkDocumentNew, out pfSaveCanceled);
                                    if (ErrorHandler.Failed(hr))
                                        return hr;
                                }
                                break;

                            case tagVSQuerySaveResult.QSR_ForceSaveAs:
                                {
                                    // Call the shell to do the SaveAS for us
                                    IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
                                    hr = uiShell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SaveAs, (IPersistFileFormat)this, fileName, out pbstrMkDocumentNew, out pfSaveCanceled);
                                    if (ErrorHandler.Failed(hr))
                                        return hr;
                                }
                                break;

                            case tagVSQuerySaveResult.QSR_NoSave_Continue:
                                // In this case there is nothing to do.
                                break;

                            default:
                                throw new NotSupportedException("Unsupported result from QEQS");
                        }
                        break;
                    }
                case VSSAVEFLAGS.VSSAVE_SaveAs:
                case VSSAVEFLAGS.VSSAVE_SaveCopyAs:
                    {
                        // Make sure the file name as the right extension
                        if (String.Compare(MyExtension, System.IO.Path.GetExtension(fileName), true, CultureInfo.CurrentCulture) != 0)
                        {
                            fileName += MyExtension;
                        }
                        // Call the shell to do the save for us
                        IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
                        hr = uiShell.SaveDocDataToFile(dwSave, (IPersistFileFormat)this, fileName, out pbstrMkDocumentNew, out pfSaveCanceled);
                        if (ErrorHandler.Failed(hr))
                            return hr;
                        break;
                    }
                default:
                    throw new ArgumentException("Unsupported Save flag");
            };

            return VSConstants.S_OK;
        }

        public int GetEditorCaption(READONLYSTATUS dwReadOnly, out string pbstrEditorCaption)
        {
            throw new NotImplementedException();
        }

        public int Close()
        {
            return VSConstants.S_OK;
        }

        public int GetLastActiveView(out IVsTextView ppView)
        {
            throw new NotImplementedException();
        }

        public int OnRegisterDocData(uint docCookie, IVsHierarchy pHierNew, uint itemidNew)
        {
            return VSConstants.S_OK;
        }

        public int RenameDocData(uint grfAttribs, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
        {
            return VSConstants.S_OK;
        }

        public int IsDocDataReloadable(out int pfReloadable)
        {
            pfReloadable = 0; //bylo 1 
            return VSConstants.S_OK;
        }

        public int ReloadDocData(uint grfFlags)
        {
            return ((IPersistFileFormat)this).Load(fileName, grfFlags, 0);
        }

        public float DefaultTabStop
        {
            get; set;
        } //TODO

        public ITextRange Range
        {
            get; private set;
        } //TODO

        public ITextSelection Selection
        {
            get; private set;
        } //TODO

        public int SelectionProperties { get; set; } //TODO

        public int FindText(string textToFind)
        {
            throw new NotImplementedException();
        }

        public int SetText(string textToSet)
        {
            return this.TypeText(textToSet);
        }

        public int TypeText(string textToType)
        {
            throw new NotImplementedException();
        }

        public int Cut()
        {
            throw new NotImplementedException();
        }

        public int Copy()
        {
            throw new NotImplementedException();
        }

        public int Paste()
        {
            throw new NotImplementedException();
        }

        public int Delete(long unit, long count)
        {
            throw new NotImplementedException();
        }

        public int MoveUp(int unit, int count, int extend)
        {
            throw new NotImplementedException();
        }

        public int MoveDown(int unit, int count, int extend)
        {
            throw new NotImplementedException();
        }

        public int MoveLeft(int unit, int count, int extend)
        {
            throw new NotImplementedException();
        }

        public int MoveRight(int unit, int count, int extend)
        {
            throw new NotImplementedException();
        }

        public int EndKey(int unit, int extend)
        {
            throw new NotImplementedException();
        }

        public int HomeKey(int unit, int extend)
        {
            throw new NotImplementedException();
        }

        int IPersist.GetClassID(out Guid pClassID)
        {
            pClassID = GuidList.guidPackageExtjsEditorFactory;
            return VSConstants.S_OK;
        }

        public int IsDirty(out int pfIsDirty)
        {
            pfIsDirty = isDirty ? 1 : 0;
            return VSConstants.S_OK;
        }

        public int InitNew(uint nFormatIndex)
        {
            if (nFormatIndex != MyFormat)
            {
                return VSConstants.E_INVALIDARG;
            }
            // until someone change the file, we can consider it not dirty as
            // the user would be annoyed if we prompt him to save an empty file
            isDirty = false;
            return VSConstants.S_OK;
        }

        public int Load(string pszFilename, uint grfMode, int fReadOnly)
        {
            fileName = pszFilename;
            editorControl.TextBox.Text = System.IO.File.ReadAllText(pszFilename);
            return VSConstants.S_OK;
        }

        public int Save(string pszFilename, int fRemember, uint nFormatIndex)
        {
            System.IO.File.WriteAllText(pszFilename, editorControl.TextBox.Text);
            return VSConstants.S_OK;
        }

        public int SaveCompleted(string pszFilename)
        {
            return VSConstants.S_OK;
        }

        public int GetCurFile(out string ppszFilename, out uint pnFormatIndex)
        {
            pnFormatIndex = MyFormat;
            ppszFilename = fileName;
            return VSConstants.S_OK;

        }

        public int GetFormatList(out string ppszFormatList)
        {

            char Endline = (char)'\n';
            string FormatList = string.Format(CultureInfo.InvariantCulture, "ExtJS Editor (*{0}){1}*{0}{1}{1}", MyExtension, Endline);
            ppszFormatList = FormatList;
            return VSConstants.S_OK;
        }

        int IPersistFileFormat.GetClassID(out Guid pClassID)
        {
            pClassID = GuidList.guidPackageExtjsEditorFactory;
            return VSConstants.S_OK;
        }
    }
}